﻿using System.Runtime.InteropServices;
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;

namespace PCANLINClass
{
    /// <summary>
    /// this class is for PCANLIN only.
    /// it has below function
    /// 1. by giving the serial port, opens the link to PCANLIN and set PCANLIN in slave mode.
    /// 2. anytime get the response from PCANLIN, parse it as required.
    /// 3. send the LIN message according to request (response to a LIN header).
    /// 4. close the serial connection to PCANLIN
    /// 
    /// </summary>

    public class PCANLIN {
        private static LOGDEBUG myLog = new LOGDEBUG();
        private Serial iSerial;
        public PCANLIN(string strCOM){
            iSerial = new Serial(strCOM);
        }

        private enum LOGType{
            INFO,
            WARNING,
            ERROR
        };
        #region Private Class: LIN; LOGDEBUG; Serial;
        private class LIN
        {

        }

        private class PCANLINFrame {
            public byte stx;
            public byte sq;
            //public int 
            public byte[] data;
        }

        private class LOGDEBUG {
            private string logFile;
            private string logTime;
            private FileInfo fLog;

            public LOGDEBUG() {
                logTime = DateTime.Now.ToString("_yyMMdd");
                this.logFile = "log" + logTime + ".log";
                fLog = new FileInfo(this.logFile);
            }

            private string getNowString()
            {
                string s;
                s = DateTime.Now.ToString("yy-MM-dd：hh.mm.ss: --");
                return s;
            }

            public Boolean appendLogText(string text, LOGType logType = LOGType.INFO)
            {
                string prefix;
                string finalString;

                switch (logType)
                {
                    case LOGType.ERROR: prefix = "[ERROR] "; break;
                    case LOGType.INFO: prefix = "[INFO] "; break;
                    case LOGType.WARNING: prefix = "[WARNING] "; break;
                    default:
                        prefix = "[ERROR]"; break;

                }

                finalString = this.getNowString() + prefix + text;
                StreamWriter sw = fLog.AppendText();
                try
                {
                    if (!fLog.Exists)
                    {
                        fLog.Create().Close();
                    }
                    sw.WriteLine(finalString);
                    return true;
                }
                catch (Exception err)
                {
                    //only could write it down in the status bar.
                    return false;
                }
                finally
                {
                    sw.Flush();
                    sw.Close();
                }
            }

        }

        private class Serial
        {
            private System.IO.Ports.SerialPort mySerialPort;
            //private int dataReceived;
            private string strFile;
            private Timer msTimer;
            private byte[] serReceiveBuffer;
            private volatile Boolean receiveForwardMsgFlag;
            private volatile Boolean receiveCommandAnswerFlag;
            private volatile Boolean newFrameFlag;
            private int msgLength;
            private int curLength;
            private int curBytePos;

            public Serial(string portName) {
                //init the serial port as the required parameters.
                mySerialPort = new System.IO.Ports.SerialPort();
                mySerialPort.PortName = portName;
                mySerialPort.BaudRate = 38400;
                mySerialPort.DataBits = 8;
                mySerialPort.Parity = System.IO.Ports.Parity.None;
                mySerialPort.StopBits = System.IO.Ports.StopBits.One;
                mySerialPort.Handshake = System.IO.Ports.Handshake.None;
                curBytePos = 0;
                //serReceiveBuffer = new int[5,]

                PCANLINFrame frame = new PCANLINFrame();

                //new Frame flag is true when 02 as stx received.
                this.newFrameFlag = true;

                msTimer = new System.Timers.Timer();
                msTimer.Elapsed += new ElapsedEventHandler(TimeEvent);
                msTimer.Interval = 5;
                mySerialPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(mySerialPort_DataReceived);
            }

            //public int getDataReceived() {
            //    return this.dataReceived;
            //}
            #region Class Serial: public function
            public Boolean openPort() {
                try
                {
                    myLog.appendLogText("trying to open serial port: " + this.mySerialPort.PortName);
                    this.mySerialPort.Open();
                }
                catch (Exception err)
                {
                    //write down traces.
                    myLog.appendLogText("open serial port failed: [" + err.Message + "]");
                    return false;
                }
                myLog.appendLogText("serial port opened.");
                return true;
            
            }

            public Boolean closePort() {
                try
                {
                    myLog.appendLogText("trying to close serial port: " + this.mySerialPort.PortName);
                    this.mySerialPort.Close();
                }
                catch (Exception err) {
                    myLog.appendLogText("close serial port failed: [" + err.Message + "]");
                    return false;
                }
                myLog.appendLogText("serial port closed");
                return true;
            }
            #endregion
            private void mySerialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) { 
                //we have data in the buffer field.
                string strNow;
                //string strFile;
                string strTmp;
                byte receiveData;
                myLog.appendLogText("received data");
                strNow = DateTime.Now.ToString("[yy-MM-dd]_hh.mm.ss: ");


                for (int count = 1; count < mySerialPort.BytesToRead; count++)
                {
                    receiveData = (byte)mySerialPort.ReadByte();
                    strTmp = receiveData.ToString("x") + "\n";
                    this.strFile += strTmp;
                }
                myLog.appendLogText("[" + DateTime.Now.ToString("[yy-MM-dd]_hh.mm.ss: ") + "]:" + this.strFile + "\n");
            }

            private void TimeEvent(object source, ElapsedEventArgs e)
            {
                byte receiveData;
                for (int count = 1; count < mySerialPort.BytesToRead; count++) {
                    this.msgLength = this.msgLength + 1;
                    receiveData = (byte)mySerialPort.ReadByte();
                    if (receiveData == 2 && this.newFrameFlag == true)
                    {
                        //in this case, means we have met the start of a new frame. then we need to do is internal message analyze as protocol defined.
                        //mark the flag that we are now parse the message, instead of receive new message.
                        this.newFrameFlag = false;
                        this.serReceiveBuffer[this.curBytePos] = receiveData;
                        this.curBytePos += 1;
                        myLog.appendLogText(receiveData.ToString("x"));
                    }
                    else { 
                        // need to parse the  
                        this.serReceiveBuffer[this.curBytePos] = receiveData;
                        if(curBytePos == 1 ){
                            //means we are now paring the sequence code byte.
                            
                            
                        }
                        myLog.appendLogText(receiveData.ToString("x"));
                    }
                }
                
            
            }
        }

        #endregion Private class

        # region public function
        public Boolean openPort()
        {
            try
            {
                if (iSerial.openPort()) {
                }

            }
            catch (Exception err) {

                return false;
            }

            return true;
        }

        public Boolean closePort() {
            try
            {
                if (iSerial.closePort())
                {
                }

            }
            catch (Exception err)
            {

                return false;
            }

            return true;

        }
        # endregion
    }

}
